﻿
using GCC.APIContainer.Auth.Results;
using GCC.Container.Abstracts;
using GCC.Container.LogProviders;

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;


namespace GCC.APIContainer.Auth.Filter
{
    /// <summary>
    /// 容器权限过滤器
    /// </summary>
    public class GCCAuthFilter : IAuthorizationFilter
    {
        /// <summary>
        /// 日志
        /// </summary>
        private static readonly AbsGCCLogger? _logger = GCCLoggerProvider.Instance?.CreateLogger<GCCAuthFilter>();
        internal static AuthSettingsInfo? AuthConfig { get; set; }

        private readonly string[] _roles;
        private readonly bool _allowOffline;
        private readonly AbsGCCContext _context;
        /// <summary>
        /// 容器权限过滤器
        /// </summary>
        /// <param name="roles">允许的角色</param>
        /// <param name="allowOffline">是否允许离线</param>
        /// <param name="context">上下文</param>
        public GCCAuthFilter(string[] roles, bool allowOffline, AbsGCCContext context)
        {
            this._roles = roles;
            this._allowOffline = allowOffline;
            this._context = context;
        }
        /// <summary>
        /// 验证发生时触发的函数
        /// </summary>
        /// <param name="filterContext"></param>
        public void OnAuthorization(AuthorizationFilterContext filterContext)
        {
            try
            {
                if (AuthConfig is null)
                {
                    return;
                }
                if (filterContext == null || filterContext.Filters.Any(item => item is IAllowAnonymous))
                {
                    return;
                }

                if (!filterContext.HttpContext.Request.Headers.TryGetValue(AuthConfig.HttpHeaderKey, out var token))
                {
                    filterContext.Result = new ForbidResult();
                    return;
                }
                GCCUserInfo? userInfo = null;
                if (this._allowOffline)
                {
                    userInfo = _context.TryGet<GCCUserInfo>(token).Result;
                    if (userInfo is null)
                    {
                        userInfo = _context.GetUserByToken(token).Result;
                        if (userInfo is not null)
                        {
                            _context.Set(token, userInfo, new CacheItemSettings()
                            {
                                SaveToAll = true,
                                AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(AuthConfig.OfflineCacheSeconds)
                            }).Wait();
                        }
                    }
                }
                else
                {
                    userInfo = _context.GetUserByToken(token).Result;
                }


                if (userInfo is null)
                {
                    filterContext.Result = new ForbidResult();
                    return;
                }


                if (userInfo.Scopes.Count != 0 && !userInfo.Scopes.Contains("*") && !userInfo.Scopes.Contains(_context?.PointName ?? ""))
                {
                    filterContext.Result = new ForbidResult();
                    return;
                }
                if (userInfo.Roles.Count != 0 && !userInfo.Scopes.Contains("*") && !userInfo.Roles.Intersect(this._roles).Any())
                {
                    filterContext.Result = new ForbidResult();
                    return;
                }
                return;
            }
            catch (Exception ex)
            {
                _logger?.Information($"User check faild.", exception: ex);
            }
            filterContext.Result = new ForbidResult();
        }
    }
}
